home *** CD-ROM | disk | FTP | other *** search
- /* Functions for running simple Standard File dialogs. Also includes
- modal dialog filter functions which may be useful for custom file
- dialogs, as well as a function for preseting the directory displayed
- in the next standard file dialog.
-
- Revision History:
-
- 94/01/17 aih
- - instead of taking a variable number of arguments to specify
- file types the file and folder selection function take an array
- of types to display
-
- 93/12/21 aih
- - multiple file selections returns dynamically sized handle containing
- selected files, instead of imposing arbitrary limitations
-
- 93/12/16 aih
- - commented out call to CloseWD for compatability with system 6.0
-
- 93/10/31 aih
- - added support for selecting a file or a folder from a single dialog
-
- 93/10/26 aih
- - functions fail with userCanceledErr is cancel is pressed, rather
- than returning true/false
-
- 93/10/16-17 aih
- - added multiple file selection
-
- 93/10/15 aih
- - added folder selection
-
- 93/03/10 AIH
- - Added support for system 7.0 standard file dialogs
-
- 93/03/08 AIH
- - The working directory returned by the standard file dialog is
- closed using CloseWD
-
- 91/11/14 AIH
- - Removed checks for system software version 7.0
-
- 91/05/31 AIH
- - Added function to fix standard file bug when a volume is unmounted
-
- 91/05/15 AIH
- - Clarified a few comments.
- - Removed code which was supposed to disable the OK item if the
- first character of a file's name started with a period (.). It's just
- too complicated to handle all possibilities (for instance, pasting a
- name starting with a period into the dialog). Besides, the Finder
- allows file names to start with a period, so does my application
- have to worry about it? (Names starting with periods are reserved
- for drivers, like ".Sony"; opening or closing a file with the same
- name as a driver can cause serious disruptions to normal operations.)
-
- 91/04/17 AIH
- - Dialog library is used to access fields of dialogs
-
- 91/03/06 AIH
- - The default button's title is set when the dialog's hook function is
- called with item -1, instead of in the dialog's filter procedure (which
- formerly caused an annoying flicker)
- - Default button's outline is only drawn if we're running on a pre-7.0
- system (7.0 takes care of the outline for us)
-
- 91/01/05 Ari Halberstadt
- - Inserted this standard header in all files
-
- 90/11/14 Ari Halberstadt
- - Created this file */
-
- #include <string.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <Script.h>
- #include "pstr.h"
- #include "ArrayListLib.h"
- #include "ControlLib.h"
- #include "DialogLib.h"
- #include "DrawLib.h"
- #include "FileDialogLib.h"
- #include "GlobalLib.h"
- #include "KeyLib.h"
- #include "ListLib.h"
- #include "LowMemLib.h"
- #include "MacLib.h"
- #include "MemoryLib.h"
- #include "RectangleLib.h"
- #include "ResourceConstantsLib.h"
- #include "ResourceLib.h"
- #include "StringLib.h"
-
- static CStr31 gBtnTitle;
-
- /* preset the next Standard File dialog to display the directory containing
- the specified file. */
- void FileSFPreset(FileType *fp)
- {
- require(FileValid(fp));
- SetCurDirStore(fp->dir);
- SetSFSaveDisk(-fp->vol);
- }
-
- /* This routine should be called before calling the Standard File
- package if there's any chance that SFSaveDisk might specify
- a volume that has been umounted. Standard File gets confused if this
- happens and presents an alert telling the user that a "system error"
- has occurred. This routine checks to make sure that SFSaveDisk specifies
- a volume that is still mounted. If not, it sets it to the first mounted
- volume, and it sets CurDirStore to the root directory on that volume.
- Since this is always called just before a standard file dialog is
- displayed it's also a handy place to unhilite the menus to prevent
- flickering. Adapted from Disinfectant source code by John Norstad. */
- void FileSFFix(void)
- {
- ParamBlockRec pb;
-
- HiliteMenu(0);
- memclr(&pb, sizeof(ParamBlockRec));
- pb.volumeParam.ioVRefNum = -GetSFSaveDisk();
- if (PBGetVInfo(&pb, false) != noErr) {
- pb.volumeParam.ioVolIndex = 1;
- FailOSErr(PBGetVInfo(&pb, false));
- SetSFSaveDisk(-pb.volumeParam.ioVRefNum);
- if (GetFSFCBLen() > 0)
- SetCurDirStore(fsRtDirID);
- }
- }
-
- /*---------------------------------------------------------------------------*/
-
- /* Set the title of the default button in standard file dialogs.
- You should call this function before either FileSFPut or FileSFGet.
- The next time one of these functions is called, the default button's
- title will be set to the given string. An empty or null title doesn't
- do anything to the dialog. For best results the string should be at
- most 10 characters long. The title is cleared after each standard
- file dialog. */
- void FileSFButtonTitle(const CStr31 title)
- {
- *gBtnTitle = 0;
- if (title) {
- strncpy(gBtnTitle, title, sizeof(CStr31)-1);
- gBtnTitle[sizeof(CStr31)-1] = 0;
- }
- }
-
- /*---------------------------------------------------------------------------*/
- /* dialog hook and filter functions used in all standard file dialogs */
- /*---------------------------------------------------------------------------*/
-
- /* hook for standard file dialogs */
- static pascal short OldSFHook(short item, DialogPtr dlg)
- {
- if (item == sfHookFirstCall && *gBtnTitle) {
- DlgCtlTitleSet(dlg, ok, gBtnTitle);
- *gBtnTitle = 0;
- }
- return(item);
- }
-
- /* hook for new standard file dialogs */
- static pascal short NewSFHook(short item, DialogPtr dlg, void *data)
- {
- if (GetWRefCon(dlg) == sfMainDialogRefCon)
- item = OldSFHook(item, dlg);
- return(item);
- }
-
- /* Modal dialog filter for standard file dialogs. This handles update
- and activate events to prevent those events from constantly being posted
- to the event queue and thus locking out other applications. Despite what it
- says in "Inside Macintosh: Files", p3-29, I may have noticed an update
- event problem on a Macintosh Plus running system 7.0. Thus, to be
- safe, a filter to handle update events is also needed by the new system
- 7.0 standard file package routines. */
- static pascal Boolean OldSFModalFilter(DialogPtr dlg, EventRecord *event,
- short *item)
- {
- switch (event->what) {
- case updateEvt:
- case activateEvt:
- if (WinIsUser((WindowPtr) event->message))
- EventExecute(event);
- break;
- }
- return(false);
- }
-
- /* modal dialog filter for new standard file dialogs */
- static pascal Boolean NewSFModalFilter(DialogPtr dlg, EventRecord *event,
- short *item, void *data)
- {
- return(OldSFModalFilter(dlg, event, item));
- }
-
- /*---------------------------------------------------------------------------*/
- /* opening files */
- /*---------------------------------------------------------------------------*/
-
- /* see description of FileSFGet */
- static void OldSFGet(FileType *fp, short ntypes, const OSType *types)
- {
- Point where;
- SFReply reply;
-
- memclr(&reply, sizeof(reply));
- DlgPositionPoint(getDlgID, &where);
- SFPGetFile(where, (StringPtr) "", NULL, ntypes, (OSType *) types,
- OldSFHook, &reply, getDlgID, OldSFModalFilter);
- FileSetWD(fp, reply.vRefNum, p2cstr(reply.fName));
- /* program_note: CloseWD seems to cause a problem when the application
- quits and returns to THINK C under Finder 6.0. THINK C
- says it can't find the volume with the project on it.
- Is this call to CloseWD really needed? */
- /* CloseWD(reply.vRefNum); /* see "Inside Macintosh: Files", 3-41 */
- if (! reply.good)
- FailOSErr(userCanceledErr);
- }
-
- /* see description of FileSFGet */
- static void NewSFGet(FileType *fp, short ntypes, const OSType *types)
- {
- Point where = { -1, -1 };
- StandardFileReply reply;
-
- memclr(&reply, sizeof(reply));
- CustomGetFile(NULL, ntypes, (OSType *) types, &reply, sfGetDialogID, where,
- NewSFHook, NewSFModalFilter, NULL, NULL, NULL);
- FileSetFSSpec(fp, &reply.sfFile);
- if (! reply.sfGood)
- FailOSErr(userCanceledErr);
- }
-
- /* Display a standard, run-of-the-mill, get file dialog box. If
- the user clicks "Open" then the selected file is stuffed in the
- 'fp' parameter. The 'ntypes' parameter should contain the number of
- types to display. The 'types' parameter is an array of types to
- display. If 'ntypes' is 0 then all files are displayed. */
- void FileSFGet(FileType *fp, short ntypes, const OSType *types)
- {
- short i;
-
- require(0 <= ntypes && ntypes < 4);
- FileSFFix();
- if (ntypes == 0)
- ntypes = -1;
- if (MacHasStandardFile())
- NewSFGet(fp, ntypes, types);
- else
- OldSFGet(fp, ntypes, types);
- ensure(FileValid(fp));
- }
-
- /*---------------------------------------------------------------------------*/
- /* saving files */
- /*---------------------------------------------------------------------------*/
-
- /* see description of FileSFPut */
- static void OldSFPut(FileType *fp, const Str255 prompt,
- const FilePNameType fname)
- {
- Point where;
- SFReply reply;
-
- memclr(&reply, sizeof(reply));
- DlgPositionPoint(putDlgID, &where);
- SFPPutFile(where, prompt, fname, OldSFHook, &reply, putDlgID,
- OldSFModalFilter);
- FileSetWD(fp, reply.vRefNum, p2cstr(reply.fName));
- /* program_note: see comment in OldSFGet */
- /* CloseWD(reply.vRefNum); /* see "Inside Macintosh: Files", p3-41 */
- if (! reply.good)
- FailOSErr(userCanceledErr);
- }
-
- /* see description of FileSFPut */
- static void NewSFPut(FileType *fp, const Str255 prompt, const FilePNameType fname)
- {
- Point where = { -1, -1 };
- StandardFileReply reply;
-
- memclr(&reply, sizeof(reply));
- CustomPutFile(prompt, fname, &reply, sfPutDialogID, where,
- NewSFHook, NewSFModalFilter, NULL, NULL, NULL);
- FileSetFSSpec(fp, &reply.sfFile);
- if (! reply.sfGood)
- FailOSErr(userCanceledErr);
- }
-
- /* Display a standard file put dialog. The 'prompt' parameter
- should contain the prompt for the dialog. The 'fname' parameter
- should contain the original name of the file. */
- void FileSFPut(FileType *fp, const CStr255 prompt, const FileNameType fname)
- {
- Str255 pprompt;
- FilePNameType pfname;
-
- require(StrValid(prompt, sizeof(CStr255)));
- require(StrValid(fname, sizeof(FileNameType)));
- c2pstrcpy(pprompt, prompt);
- c2pstrcpy(pfname, fname);
- FileSFFix();
- if (MacHasStandardFile())
- NewSFPut(fp, pprompt, pfname);
- else
- OldSFPut(fp, pprompt, pfname);
- ensure(FileValid(fp));
- }
-
- /*---------------------------------------------------------------------------*/
- /* select a folder using a standard file dialog, adapted from "inside
- macintosh: files", p3-12 and p3-34 */
- /*---------------------------------------------------------------------------*/
-
- /* additional items (the "msf" prefix stands for "modified standard file") */
- #define msfSelectFolderButton (10)
- #define msfSelectFolderPromptText (11)
-
- /* call-backs for file selection */
- typedef struct {
- FileFilterYDProcPtr filter; /* file filter function */
- DlgHookYDProcPtr hook; /* dialog hook function */
- ModalFilterYDProcPtr modal; /* modal dialog filter function */
- ActivateYDProcPtr activate; /* item activation function */
- short *activelist; /* list of items to activate */
- void *data; /* user supplied data */
- } FileCallBacks;
-
- /* data used when selecting a single folder or multiple folders */
- typedef struct {
- FileCallBacks callback; /* call-back routines */
- StandardFileReply *reply; /* the reply record */
- Boolean good; /* true if selected a folder */
- FileNameType folder; /* name of folder */
- short ntypes; /* number of file types to display */
- } FolderSelectData;
-
- /* set the title of the select folder button to the name of the
- currently selected folder */
- static void FolderSetButton(ControlHandle btn, const CStr255 name)
- {
- Rect r;
- short len;
- CStr31 fmt;
- CStr255 title;
- GrafPtr port = NULL;
- RgnHandle clip;
-
- /* make the button's title fit inside the button */
- GetPort(&port);
- SetPort(CtlWindow(btn));
- CtlRect(btn, &r);
- ResStr31(RLS_BUTTON, RLS_BUTTON_SELECT, fmt);
- len = sprintf(title, fmt, name);
- (void) TruncText(RectWidth(&r) - CharWidth(' '), title, &len, smTruncMiddle);
- title[len] = 0;
-
- /* set control's title; extra code is to prevent annoying flicker */
-
- /* prevent drawing of borders of control */
- clip = BeginRgn();
- GetClip(clip);
- CtlRect(btn, &r);
- InsetRect(&r, 2, 2);
- ClipRect(&r);
-
- /* set control's title */
- CtlTitleSet(btn, title);
- CtlRect(btn, &r);
- ValidRect(&r);
-
- /* restore environment */
- SetClip(clip);
- EndRgn(clip);
- SetPort(port);
- }
-
- /* filter files */
- static pascal Boolean FolderFilter(ParamBlockRec *pb, void *data)
- {
- FolderSelectData *fsdata = data;
- Boolean hide = false;
-
- if (fsdata->callback.filter)
- hide = fsdata->callback.filter(pb, fsdata->callback.data);
- return(hide);
- }
-
- /* determine the name of the selected folder */
- static void FolderSelectedName(short item, DialogPtr dlg,
- FolderSelectData *fsdata, FileNameType name)
- {
- CInfoPBRec cat;
-
- *name = 0;
- require(GetWRefCon(dlg) == sfMainDialogRefCon);
- if (item == sfHookFirstCall) {
- /* get name of default folder */
- memclr(&cat, sizeof(CInfoPBRec));
- cat.dirInfo.ioVRefNum = -GetSFSaveDisk();
- cat.dirInfo.ioDrDirID = GetCurDirStore();
- cat.dirInfo.ioNamePtr = (StringPtr) name;
- cat.dirInfo.ioFDirIndex = -1;
- FailOSErr(PBGetCatInfo(&cat, false));
- }
- else {
- /* get name of selected folder */
- if (fsdata->reply->sfIsFolder || fsdata->reply->sfIsVolume)
- pstrcpy((StringPtr) name, fsdata->reply->sfFile.name);
- else {
- memclr(&cat, sizeof(CInfoPBRec));
- cat.dirInfo.ioVRefNum = fsdata->reply->sfFile.vRefNum;
- cat.dirInfo.ioDrDirID = fsdata->reply->sfFile.parID;
- cat.dirInfo.ioNamePtr = (StringPtr) name;
- cat.dirInfo.ioFDirIndex = -1;
- FailOSErr(PBGetCatInfo(&cat, false));
- }
- }
- p2cstr((StringPtr) name);
- }
-
- /* handle the select folder button */
- static pascal short FolderHook(short item, DialogPtr dlg, void *data)
- {
- FolderSelectData *fsdata = data;
- FileNameType name; /* name of selected folder */
- CStr31 prompt; /* prompt for dialog */
- short promptid; /* index of prompt string */
-
- if (fsdata->callback.hook)
- item = fsdata->callback.hook(item, dlg, fsdata->callback.data);
- if (GetWRefCon(dlg) == sfMainDialogRefCon) {
- if (item == sfHookFirstCall) {
- /* set prompt string */
- promptid = (fsdata->ntypes == 0 ? RLS_SF_SELECT_FOLDER :
- RLS_SF_SELECT_FILE_OR_FOLDER);
- ResStrLen(RLS_SF, promptid, prompt, sizeof(prompt));
- DlgTextSet(dlg, msfSelectFolderPromptText, prompt);
- }
- /* get name of selected item */
- FolderSelectedName(item, dlg, fsdata, name);
- /* set button title */
- if (strcmp(name, fsdata->folder) != 0) {
- strcpy(fsdata->folder, name);
- FolderSetButton(DlgCtl(dlg, msfSelectFolderButton), name);
- }
- /* force return by faking a cancel */
- if (item == msfSelectFolderButton) {
- item = sfItemCancelButton;
- fsdata->good = true;
- }
- }
- return(item);
- }
-
- /* handle the keyboard equivalent for the select folder button */
- static pascal Boolean FolderModalFilter(DialogPtr dlg, EventRecord *event,
- short *item, void *data)
- {
- FolderSelectData *fsdata = data;
- Boolean result = false;
-
- if (fsdata->callback.modal)
- result = fsdata->callback.modal(dlg, event, item, fsdata->callback.data);
- if (! result)
- result = NewSFModalFilter(dlg, event, item, NULL);
- if (! result && GetWRefCon(dlg) == sfMainDialogRefCon) {
- if (event->what == keyDown && (event->modifiers & cmdKey) != 0) {
- char key = event->message;
- if (key == 's' || key == 'S') {
- DlgFlashButton(dlg, msfSelectFolderButton);
- *item = msfSelectFolderButton;
- result = true;
- }
- }
- }
- return(result);
- }
-
- /* Select a folder or a file using a custom file dialog. Special file filter,
- dialog hook, and modal dialog filter functions are already called to support
- the customized standard file dialog. In addition, the caller can specify
- its own call-back functions to be called before the special
- folder selection call-back functions are called in this file.
- The 'data' parameter may contain any data to be passed to the
- additional call-back functions. The 'ntypes' parameter specifies the
- number of file types to display, and types contains the types to display.
- If 'ntypes' is -1, all files are displayed, while if 'ntypes' is 0 only
- folders are displayed. */
- void FileOrFolderSelectCustom(FileType *fp, short ntypes, const OSType *types,
- FileFilterYDProcPtr filter,
- DlgHookYDProcPtr hook,
- ModalFilterYDProcPtr modal,
- short *activelist,
- ActivateYDProcPtr activate,
- void *data)
- {
- Point where = { -1, -1 };
- StandardFileReply reply;
- FolderSelectData fsdata;
-
- require(MacHasStandardFile());
- require(-1 <= ntypes && ntypes < 4);
- memclr(&reply, sizeof(reply));
- memclr(&fsdata, sizeof(FolderSelectData));
- fsdata.callback.filter = filter;
- fsdata.callback.hook = hook;
- fsdata.callback.modal = modal;
- fsdata.callback.data = data;
- fsdata.ntypes = ntypes;
- fsdata.reply = &reply;
- FileSFFix();
- CustomGetFile(FolderFilter, ntypes, (OSType *) types, &reply, RLD_FOLDER,
- where, FolderHook, FolderModalFilter, activelist, activate, &fsdata);
- if (fsdata.good && reply.sfIsVolume)
- pstrcat(reply.sfFile.name, (StringPtr) "\p:");
- FileSetFSSpec(fp, &reply.sfFile);
- if (! fsdata.good && ! reply.sfGood)
- FailOSErr(userCanceledErr);
- }
-
- /* select a file or folder */
- void FileOrFolderSelect(FileType *fp, short ntypes, const OSType *types)
- {
- FileOrFolderSelectCustom(fp, ntypes, types, NULL, NULL, NULL, NULL, NULL, NULL);
- }
-
- /*---------------------------------------------------------------------------*/
- /* Selecting multiple files. The standard file dialog is modified to
- include an item listing selected files, and buttons to manipulate
- the list of selected files. */
- /*---------------------------------------------------------------------------*/
-
- /* additional items (the "msf" prefix stands for "modified standard file") */
- enum {
- msfItemListUser = 11,
- msfItemAddButton,
- msfItemRemoveButton,
- msfItemDoneButton
- };
-
- /* data used when selecting multiple files */
- typedef struct {
- FileCallBacks callback; /* call-back routines */
- StandardFileReply *reply; /* the reply record */
- ListHandle list; /* handle to our list */
- Boolean good; /* true if the user didn't cancel */
- Boolean active; /* true if our list is active */
- ArrayListHandle files; /* list of selected files */
- } FileMultipleData;
-
- /* filter out files that have already been selected */
- static pascal Boolean FileMultipleFilter(ParamBlockRec *pb, void *data)
- {
- CInfoPBRec *cat = (CInfoPBRec *) pb; /* this isn't documented but seems to work */
- FileMultipleData *fsdata = data;
- FileHandle files = NULL;
- size_t nfiles = 0;
- SignedByte state = 0;
- Boolean hide = false;
- size_t i;
-
- if (fsdata->callback.filter)
- hide = fsdata->callback.filter(pb, fsdata->callback.data);
- files = ArrayListGetHandle(fsdata->files);
- nfiles = ArrayListCount(fsdata->files);
- state = HandleLock(files);
- for (i = 0; ! hide && i < nfiles; i++) {
- if ((pb->fileParam.ioFlAttrib & (1 << kFolderBit)) == 0) {
- /* a file */
- hide = ( cat->hFileInfo.ioVRefNum == (*files)[i].vol &&
- cat->hFileInfo.ioFlParID == (*files)[i].dir &&
- pstrcmp(cat->hFileInfo.ioNamePtr, (*files)[i].pnm) == 0);
- }
- else {
- /* a folder */
- hide = ( cat->dirInfo.ioVRefNum == (*files)[i].vol &&
- cat->dirInfo.ioDrParID == (*files)[i].dir &&
- pstrcmp(cat->dirInfo.ioNamePtr, (*files)[i].pnm) == 0);
- }
- }
- HandleRestore(files, state);
- return(hide);
- }
-
- /* change the hiliting of the list to indicate that it is active and
- is receiving keyboard input */
- static pascal void FileMultipleActivate(DialogPtr dlg, short item,
- Boolean activating, void *data)
- {
- FileMultipleData *fsdata = data;
- Cell cell;
-
- activating = (activating != 0); /* uses 0xFF for true, so convert to Boolean */
- if (fsdata->callback.activate)
- fsdata->callback.activate(dlg, item, activating, fsdata->callback.data);
- if (item == msfItemListUser) {
- ListFocus(fsdata->list, activating);
- fsdata->active = activating;
- }
- }
-
- static pascal short FileMultipleHook(short item, DialogPtr dlg, void *data)
- {
- FileMultipleData *fsdata = data;
- FileNameType name; /* name of selected folder */
- Rect view; /* view rectangle of list */
- Rect bounds = { 0, 0, 0, 1 }; /* data bounds of list */
- Cell cell = { 0, 0 }; /* for accessing cells in list */
- int index; /* index for inserting/removing from list */
- FileType tmpfp; /* for inserting file without locking handle */
-
- /* call user supplied hook */
- if (fsdata->callback.hook)
- item = fsdata->callback.hook(item, dlg, fsdata->callback.data);
- if (GetWRefCon(dlg) == sfMainDialogRefCon) {
- /* get name of selected item */
- p2cstrcpy(name, fsdata->reply->sfFile.name);
- /* adjust buttons */
- if (fsdata->list) {
- cell.h = cell.v = 0;
- DlgCtlEnableSet(dlg, msfItemAddButton,
- ! fsdata->reply->sfIsVolume &&
- *fsdata->reply->sfFile.name > 0);
- DlgCtlEnableSet(dlg, msfItemRemoveButton,
- fsdata->active && LGetSelect(true, &cell, fsdata->list));
- /* program_note: The item sfItemOpenButton should be disabled if
- a file is selected (i.e., not a folder or volume)
- so that the Done and Cancel buttons will be
- the only ways to exit the dialog. But I can't
- figure out how to disable the button (disabling
- it here is immediately overridden when the standard
- file package reenables it). This means that clicking
- the Open button when a file is selected is equivalent
- to clicking the Done button. */
- }
- switch (item) {
- case sfHookFirstCall:
- /* create our list */
- cell.h = cell.v = 0;
- DlgBox(dlg, msfItemListUser, &view);
- fsdata->list = ListBegin(&view, &bounds, cell, 0, dlg,
- true, false, false, true, true);
- break;
- case sfHookLastCall:
- /* dispose of our list */
- ListEnd(fsdata->list);
- fsdata->list = NULL;
- break;
- case msfItemAddButton:
- /* add file to list */
- check(*name != 0);
- check(! fsdata->active);
- check(! fsdata->reply->sfIsVolume);
- /* insert name into display list */
- (void) ListSearchInsert(fsdata->list, name, strlen(name), &cell);
- cell.h = 0;
- index = cell.v = LAddRow(1, cell.v, fsdata->list);
- LSetCell(name, strlen(name), cell, fsdata->list);
- /* insert into list of files, at index corresponding to
- item in display list, which makes it easy to remove
- the item when the user hits msfItemRemoveButton */
- FileSetFSSpec(&tmpfp, &fsdata->reply->sfFile);
- ArrayListInsert(fsdata->files, index);
- ArrayListSet(fsdata->files, index, &tmpfp);
- item = sfHookRebuildList;
- break;
- case msfItemRemoveButton:
- /* remove selected cells from list */
- check(fsdata->active);
- cell.h = cell.v = 0;
- while (LGetSelect(true, &cell, fsdata->list)) {
- index = cell.v;
- LDelRow(1, index, fsdata->list);
- ArrayListDelete(fsdata->files, index);
- cell.h = cell.v = 0;
- }
- item = sfHookRebuildList;
- break;
- case msfItemDoneButton:
- /* force return by faking a cancel */
- item = sfItemCancelButton;
- fsdata->good = true;
- break;
- }
- }
- return(item);
- }
-
- static pascal Boolean FileMultipleModalFilter(DialogPtr dlg, EventRecord *event,
- short *item, void *data)
- {
- FileMultipleData *fsdata = data;
- Boolean result = false;
-
- if (fsdata->callback.modal)
- result = fsdata->callback.modal(dlg, event, item, fsdata->callback.data);
- if (! result)
- result = NewSFModalFilter(dlg, event, item, NULL);
- if (! result) {
- /* handle events in the list and keyboard equivalents of addtional
- buttons */
- switch (event->what) {
- case updateEvt:
- ListUpdate(fsdata->list);
- FrameDraw(ListFrame(fsdata->list));
- break;
- case activateEvt:
- FileMultipleActivate(dlg, msfItemListUser,
- fsdata->active && (event->modifiers & activeFlag) != 0, fsdata);
- ListActivate(fsdata->list, (event->modifiers & activeFlag) != 0);
- break;
- case mouseDown:
- if (GetWRefCon(dlg) == sfMainDialogRefCon) {
- Point where = event->where;
- GlobalToPort(&where, dlg);
- if (ListWithin(fsdata->list, where))
- ListMouseDown(fsdata->list, event);
- }
- break;
- case keyDown:
- case autoKey:
- if (GetWRefCon(dlg) == sfMainDialogRefCon) {
- char key = event->message;
- if ((event->modifiers & cmdKey) != 0) {
- /* handle keyboard equivalents for additional buttons */
- switch (key) {
- case 'a':
- case 'A':
- if (DlgCtlEnabled(dlg, msfItemAddButton)) {
- *item = msfItemAddButton;
- DlgFlashButton(dlg, *item);
- }
- result = true;
- break;
- case 'r':
- case 'R':
- if (DlgCtlEnabled(dlg, msfItemRemoveButton)) {
- *item = msfItemRemoveButton;
- DlgFlashButton(dlg, *item);
- }
- result = true;
- break;
- }
- }
- else if (fsdata->active && key != tabKey) {
- /* handle keyboard input in list */
- ListKeyDown(fsdata->list, event);
- result = true;
- }
- }
- break;
- }
- }
- return(result);
- }
-
- /* Select multiple files. Returns handle containing an array of the
- selected files. The number of items in the array is
- HandleSize(files) / sizeof(FileType) */
- FileHandle FileSelectMultipleCustom(
- FileFilterYDProcPtr filter,
- DlgHookYDProcPtr hook,
- ModalFilterYDProcPtr modal,
- short *activelist,
- ActivateYDProcPtr activate,
- void *data)
- {
- short msfactivelist[3] = { 2, sfItemFileListUser, msfItemListUser };
- Point where = { -1, -1 };
- SFTypeList types;
- StandardFileReply reply;
- FileMultipleData fsdata;
- FileHandle files = NULL;
-
- require(MacHasStandardFile());
- TRY {
- memclr(&reply, sizeof(reply));
- memclr(&fsdata, sizeof(FileMultipleData));
- fsdata.callback.filter = filter;
- fsdata.callback.hook = hook;
- fsdata.callback.modal = modal;
- fsdata.callback.activate = activate;
- fsdata.callback.data = data;
- fsdata.reply = &reply;
- fsdata.files = ArrayListBegin(sizeof(FileType));
- if (! activelist)
- activelist = msfactivelist;
- FileSFFix();
- CustomGetFile(FileMultipleFilter, -1, types, &reply, RLD_MULTIPLE, where,
- FileMultipleHook, FileMultipleModalFilter,
- activelist, FileMultipleActivate, &fsdata);
- if (! fsdata.good)
- FailOSErr(userCanceledErr);
- files = ArrayListGetHandle(fsdata.files);
- ArrayListDetachHandle(fsdata.files);
- } CATCH {
- ArrayListEnd(fsdata.files);
- } ENDTRY;
- return(files);
- }
-
- /* select multiple files, return handle containing selected files */
- FileHandle FileSelectMultiple(void)
- {
- return(FileSelectMultipleCustom(NULL, NULL, NULL, NULL, NULL, NULL));
- }
-